﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TcpClient
{
    public delegate void tcpServerRcvMsg_String(tcpClient client, string strRcvMsg, int iRcvDataCount);
    public delegate void tcpServerRcvMsg_Byte(tcpClient client, byte []RcvMsg,int iRcvDataCount);
    public delegate void tcpServerError(tcpClient client,Exception e);
    public delegate void tcpConnectState(tcpClient client, bool bState);

    public class tcpClient
    {
        public event tcpServerRcvMsg_String OnRcvMsg_String = null;
        public event tcpServerRcvMsg_Byte OnRcvMsg_Byte = null;
        public event tcpServerError OnError = null;
        public event tcpConnectState OnConnectStateChange = null;

        /// <summary>
        /// ////////////////////////////////////////////////////////////////////////////
        /// </summary>
        private string m_strIP;
        private int m_iPort;
        IPEndPoint m_IPEndPoint;
        Socket m_Client = null;
        bool m_bConnectServerState = false;


        //线程结束后，设置此事件
        private AutoResetEvent m_DeadEvent = new AutoResetEvent(false);
        private Thread m_hRcvThread = null;
        private bool m_bStopThread = true;

        private Thread m_hAutoConnectedThread = null;
        private AutoResetEvent m_ConnectServer = new AutoResetEvent(false);

        private delegate string ConnectSocketDelegate(IPEndPoint ipep, Socket sock);
        private string ConnectSocket(IPEndPoint ipep, Socket sock)
        {
            string exmessage = "";
            try
            {
                sock.Connect(ipep);
            }
            catch (System.Exception ex)
            {
                exmessage = ex.Message;
            }
            finally
            {
            }

            return exmessage;
        }

        public void StartWork_AutoConnectServer(string strIP, int iPort)
        {
            m_strIP = strIP;
            m_iPort = iPort;

            m_bStopThread = false;
            m_bConnectServerState = false;
            m_hAutoConnectedThread = new Thread(AutoConnectedServer);

            m_hAutoConnectedThread.Start();

            m_ConnectServer.Set();
        }


        private string ConnectServer()
        {
            try
            {
                IPAddress ip = IPAddress.Parse(m_strIP);
                m_IPEndPoint = new IPEndPoint(ip, m_iPort);

                m_Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                //连接到服务器
                ConnectSocketDelegate connect = ConnectSocket;
                IAsyncResult asyncResult = connect.BeginInvoke(m_IPEndPoint, m_Client, null, null);

                bool connectSuccess = asyncResult.AsyncWaitHandle.WaitOne(1000, false);
                if (!connectSuccess)
                {
                    return "失败！错误信息：连接超时";
                }

                string exmessage = connect.EndInvoke(asyncResult);
                if (!string.IsNullOrEmpty(exmessage))
                {
                    return "失败！错误信息：" + exmessage;
                }

                m_bStopThread = false;
                m_hRcvThread = new Thread(ReceiveMsg);
                m_hRcvThread.Start();
            }
            catch (Exception ex)
            {
                return "失败！错误信息：" + ex.Message;
            }

            return "";
        }

        //如果失败，则返回失败原因
        public string StartWork_ConnectServerOnce(string strIP, int iPort)
        {
            m_strIP = strIP;
            m_iPort = iPort;
            m_bConnectServerState = false;

            return ConnectServer();
        }

        public bool IsConnected
        {
            get
            {
                return this.m_Client != null && this.m_Client.Connected && m_bConnectServerState == true;
            }
        }

        //发送数据。如果发送失败，则返回失败原因
        public string SendMsg(byte[] msg)
        {
            if (IsConnected == true)
            {
               // lock (m_Client)
                {
                    try
                    {
                        m_Client.Send(msg);
                    }
                    catch(Exception ex)
                    {
                        return "失败！错误信息：" + ex.Message;
                    }
                }
            }
            else
            {
                return "失败！错误信息：未联接服务器" ;
            }

            return "";
        }
        ////发送数据。如果发送失败，则返回失败原因
        public string SendMsg(string strMsg)
        {
            byte[] byteArray = System.Text.Encoding.Default.GetBytes(strMsg);

            return SendMsg(byteArray);
        }

        public void StopWork()
        {
            m_bStopThread = true;

            if (m_hAutoConnectedThread != null)
            {
                bool bRet = m_DeadEvent.WaitOne(1000);
                if (bRet != true)
                {
                    m_hAutoConnectedThread.Abort();
                }
                m_hAutoConnectedThread = null;
            }

            try
            {
                if (m_Client != null)
                {
                    m_Client.Shutdown(SocketShutdown.Both);

                    m_Client.Close();
                    m_Client = null;
                }
            }
            catch (Exception ex)
            {
                if (OnError != null)
                {
                    OnError(this, ex);
                }
            }

            Thread.Sleep(100);
            if (m_hRcvThread != null)
            {
                bool bRet = m_DeadEvent.WaitOne(1000);
                if (bRet != true)
                {
                    if (OnError != null)
                    {
                        OnError(this, new Exception("线程结束失败"));
                    }
                    m_hRcvThread.Abort();
                }
                m_hRcvThread = null;
            }
        }

        void AutoConnectedServer()
        {
            while (m_bStopThread != true)
            {

                if (IsConnected == false)
                {
                    ConnectServer();
                }

                Thread.Sleep(500);
            }
        }

        void ReceiveMsg()
        {
            m_DeadEvent.Reset();
            m_bConnectServerState = true;

            if (OnConnectStateChange != null)
            {
                OnConnectStateChange(this, true);
            }

            while (m_bStopThread != true)
            {
                try
                {
                    byte[] buffer = new byte[8192];
                    int n = 0;
                    //lock (m_Client)
                    {
                        n = m_Client.Receive(buffer);
                    }

                    if (n < 1)
                    {
                        if (OnError != null)
                        {
                            OnError(this, new Exception("联接断开"));
                        }


                        m_ConnectServer.Set();
                        break;
                    }
                    else
                    {
                       if (OnRcvMsg_Byte != null)
                        {
                            OnRcvMsg_Byte(this, buffer,n);
                        }

                       if (OnRcvMsg_String != null)
                        {
                            string str = System.Text.Encoding.Default.GetString(buffer,0,n);

                            OnRcvMsg_String(this, str, n);
                        }
                    }

                }
                catch (ThreadInterruptedException) { } //thread is interrupted when we quit
                catch (Exception ex)
                {
                    if (OnError != null)
                    {
                        OnError(this, ex);
                    }
                }

            }
            m_bConnectServerState = false;

            if (OnConnectStateChange != null)
            {
                OnConnectStateChange(this, false);
            }

            m_DeadEvent.Set();
        }
    }
}
